/*
 * Copyright (c) 2017-2021, Lindenis Tech. Ltd.
 * All rights reserved.
 *
 * File:
 *
 * Description:
 *
 * Author:
 *      xiaoshujun@lindeni.com
 *
 * Create Date:
 *      2021/10/19
 *
 * History:
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>

#include "osal_types.h"
#include "osal_mutex.h"
#include "osal_common.h"

#include "lindtunnel.h"
#include "ldt_error.h"
#include "sample_ldt_ioctrl_def.h"

#define LOG_TAG "sample_ldt_cli"
#include "osal_log.h"

#define LISENCE_KEY "TEST"

#define MAX_SESSION         4
#define WAIT_RESP_TIMEOUT   5000    //5s

typedef struct _sample_ldt_cli_ctx_t
{
    char                    uid_list[MAX_SESSION][32];

    lock_mutex              sid_mutex;
    int                     sid;
    int                     gid;

    int                     io_running;
    pthread_t               io_tid;

    lock_mutex              ref_lock;

} sample_ldt_cli_ctx_t;

static int sample_ldt_cli_start_ioctrl(sample_ldt_cli_ctx_t * p_ctx);
static void sample_ldt_cli_stop_ioctrl(sample_ldt_cli_ctx_t * p_ctx);

static int32_t sample_ldt_cli_on_event(void * user, LDT_MSG_e msg, void * ext1, void * ext2)
{
    sample_ldt_cli_ctx_t * p_ctx = user;

    switch (msg)
    {
        case LDT_MSG_LOGIN:
        {
            logd("client login");
            break;
        }
        case LDT_MSG_LOGOUT:
        {
            logd("client logout");
            break;
        }
        case LDT_MSG_SESSION_DISCONN:
        {
            int sid = *(int*)ext1;
            logd("device disconnect, sid: %d", sid);
            do_lock(&p_ctx->sid_mutex);
            LDT_Cli_DisconnDevice(sid);
            p_ctx->sid = -1;
            do_unlock(&p_ctx->sid_mutex);
            break;
        }
        case LDT_MSG_CHANNEL_GROUP_CREATED:
        {
            logi("new channel group sid: %d, gid: %d", *(int*)ext1, *(int*)ext2);
            p_ctx->gid = *(int*)ext2;
            sample_ldt_cli_start_ioctrl(p_ctx);
            break;
        }
        case LDT_MSG_CHANNEL_GROUP_DESTORY:
        {
            logi("channel group destory sid: %d, gid: %d", *(int*)ext1, *(int*)ext2);
            sample_ldt_cli_stop_ioctrl(p_ctx);
            break;
        }
        case LDT_MSG_SESSION_BITRATE:
        {
            int sid = *(int*)ext1;
            int bps = *(int*)ext2;
            logv("sid: %d, %d(kbps)", sid, bps >> 10);
            break;
        }
        default:
            break;
    }

    return 0;
}

static int sample_ldt_handler_channel_ioctrl(sample_ldt_cli_ctx_t * p_ctx, int32_t sid, int32_t gid,
                                    int32_t cmd, uint8_t * data)
{
    switch(cmd)
    {
        case SAMPLE_IOCTRL_DEVICE_EVENT:
        {
            sample_ldt_ioctrl_event_t * p_event = (sample_ldt_ioctrl_event_t *)data;
            logd("event type: 0x%x", p_event->type);
            return 0;
        }
        default:
            logw("unknown cmd: 0x%x", cmd);
            break;
    }

    return 0;
}

static void * thread_ldt_cli_ioctrl_recv(void *param)
{
    sample_ldt_cli_ctx_t * p_ctx = (sample_ldt_cli_ctx_t *)param;
    int ret = 0;
    int sid = p_ctx->sid;
    int gid = p_ctx->gid;
    int cmd = 0;
    uint8_t data[LDT_PKG_MAX_SIZE];
    int size = 0;

    logd("thread_ldt_cli_ioctrl_recv running ...");

    while(p_ctx->io_running)
    {
        ret = LDT_Cli_RecvIOCtrl(sid, gid, &cmd, (uint8_t *)&data, &size, 1000);
        if (ret < 0)
        {
            continue;
        }

        sample_ldt_handler_channel_ioctrl(p_ctx, sid, gid, cmd, (uint8_t *)&data);
    }

_exit_:
    LDT_Cli_FlushRecvIOCtrl(sid, gid);

    logd("thread_ldt_cli_ioctrl_recv exit");
    return 0;
}

static int sample_ldt_cli_start_ioctrl(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;

    // create ioctrl recv thread
    p_ctx->io_running = 1;
    ret = pthread_create(&p_ctx->io_tid, NULL, thread_ldt_cli_ioctrl_recv, (void *)p_ctx);
    if (ret) {
        loge("create thread_ldt_cli_ioctrl_recv failed: %s", strerror(errno));
        return LDTER_FAILED;
    }

    return LDTER_OK;
}

static void sample_ldt_cli_stop_ioctrl(sample_ldt_cli_ctx_t * p_ctx)
{
    p_ctx->io_running = 0;
    if (p_ctx->io_tid != 0)
    {
        pthread_join(p_ctx->io_tid, 0);
        p_ctx->io_tid = 0;
    }
}

static int sample_ldt_cli_add(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;
    int irmode = 0;
    int alarm_mode = 0;

    if (p_ctx->sid < 0 || p_ctx->gid < 0) {
        loge("please connect device first");
        return LDTER_FAILED;
    }

    sample_ldt_ioctrl_add_request_t req = {0};
    req.a = 100;
    req.b = 20;
    ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, SAMPLE_IOCTRL_ADD_REQ,
                            (int8_t *)&req, sizeof(req));
    if (ret < 0) {
        loge("SAMPLE_IOCTRL_ADD_REQ failed: %s", strerror(errno));
        return LDTER_FAILED;
    }

    sample_ldt_ioctrl_add_response_t resp = {0};
    int cmd = SAMPLE_IOCTRL_ADD_RESP;
    int size = sizeof(resp);
    ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                            (int8_t *)&resp, (int32_t *)&size, WAIT_RESP_TIMEOUT);
    if (ret < 0) {
        loge("SAMPLE_IOCTRL_ADD_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }

    logi("    %d + %d = %d", req.a, req.b, resp.result);

    return 0;
}

static int sample_ldt_cli_set_params(sample_ldt_cli_ctx_t * p_ctx)
{
    int ret = 0;
    int irmode = 0;
    int alarm_mode = 0;

    if (p_ctx->sid < 0 || p_ctx->gid < 0) {
        loge("please connect device first");
        return LDTER_FAILED;
    }

    /*
     * set params
     */
    {
        sample_ldt_ioctrl_set_params_request_t req = {0};
        strcpy(req.str, "hello, world");
        req.ival = 100;
        req.fval = 20.0f;
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, SAMPLE_IOCTRL_SET_PARAMS_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("SAMPLE_IOCTRL_SET_PARAMS_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        sample_ldt_ioctrl_set_params_response_t resp = {0};
        int cmd = SAMPLE_IOCTRL_SET_PARAMS_RESP;
        int size = sizeof(resp);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resp, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0 || resp.result != 0) {
            loge("SAMPLE_IOCTRL_SET_PARAMS_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }
    }

    /*
     * get params
     */
    {
        sample_ldt_ioctrl_get_params_request_t req = {0};
        ret = LDT_Cli_SendIOCtrl(p_ctx->sid, p_ctx->gid, SAMPLE_IOCTRL_GET_PARAMS_REQ,
                                (int8_t *)&req, sizeof(req));
        if (ret < 0) {
            loge("SAMPLE_IOCTRL_GET_PARAMS_REQ failed: %s", strerror(errno));
            return LDTER_FAILED;
        }

        sample_ldt_ioctrl_get_params_response_t resp = {0};
        int cmd = SAMPLE_IOCTRL_GET_PARAMS_RESP;
        int size = sizeof(resp);
        ret = LDT_Cli_WaitResp(p_ctx->sid, p_ctx->gid, cmd,
                                (int8_t *)&resp, (int32_t *)&size, WAIT_RESP_TIMEOUT);
        if (ret < 0) {
            loge("SAMPLE_IOCTRL_GET_PARAMS_RESP failed, ret: %d", ret);
            return LDTER_FAILED;
        }

        logi("    get params: %s, %d, %.1f", resp.str, resp.ival, resp.fval);
    }

    return 0;
}

static int sample_ldt_cli_process(const char * uid, const char * password)
{
    int ret = 0;
    sample_ldt_cli_ctx_t contex = {0};
    sample_ldt_cli_ctx_t * p_ctx = &contex;
    p_ctx->sid = -1;
    p_ctx->gid = -1;

    init_lock(&p_ctx->sid_mutex);
    init_lock(&p_ctx->ref_lock);

    LDT_Cfg_t config = {0};
    config.on_event = sample_ldt_cli_on_event;
    config.user = p_ctx;
    config.max_session = MAX_SESSION;
    config.timeout_ms = 5000;
    ret = LDT_Cli_Initialize(&config, LISENCE_KEY);
    if (ret < 0)
    {
        loge("LDT_Cli_Initialize failed, ret: %d", ret);
        goto _exit1_;
    }

    while(1)
    {
        int key_val = getchar();
        if (key_val <= 0) {
            continue;
        }

        if (key_val != '\n')
        {
            printf("********* key_val: %c ********** \n", key_val);
        }

        if (key_val == 'q' || key_val == 'Q')
        {
            break;
        }
        else if (key_val == 'f' || key_val == 'F')
        {
            int n = LDT_Cli_QueryDevice((char **)&p_ctx->uid_list, MAX_SESSION);
            if (n < 0)
            {
                logw("LDT_Cli_QueryDevice failed");
                continue;
            }
            logd("find device count: %d", n);
            for (; n > 0; n--)
            {
                logd("\t uid: %s", p_ctx->uid_list[n - 1]);
            }
            continue;
        }
        else if (key_val == 'c')
        {
            logd("conn device: %s, psw: %s", uid, password);
            int sid = LDT_Cli_ConnDevice(uid, password, 5000);
            if (sid < 0)
            {
                logw("LDT_Cli_ConnDevice(%s) failed", uid);
                continue;
            }
            p_ctx->sid = sid;
            logd("LDT_Cli_ConnDevice success, sid: %d", sid);
            continue;
        }
        else if (key_val == 'C')
        {
            logd("LDT_Cli_DisconnDevice success, sid: %d", p_ctx->sid);
            LDT_Cli_DisconnDevice(p_ctx->sid);
            p_ctx->sid = -1;
            continue;
        }
        else if (key_val == 'g')
        {
            do_lock(&p_ctx->sid_mutex);
            LDT_ChnGrpCfg_t grp_cfg = {0};
            int gid = LDT_Cli_CreateChnGrp(p_ctx->sid, &grp_cfg);
            if (gid < 0)
            {
                logw("LDT_Cli_CreateChnGrp failed");
                do_unlock(&p_ctx->sid_mutex);
                continue;
            }
            p_ctx->gid = gid;
            do_unlock(&p_ctx->sid_mutex);

            logd("LDT_Cli_CreateChnGrp success, gid: %d", gid);
            continue;
        }
        else if (key_val == 'G')
        {
            do_lock(&p_ctx->sid_mutex);
            if (p_ctx->sid >= 0 && p_ctx->gid >= 0)
            {
                LDT_Cli_DestroyChnGrp(p_ctx->sid, p_ctx->gid);
                logd("LDT_Cli_DestroyChnGrp, sid: %d, gid: %d", p_ctx->sid, p_ctx->gid);
            }
            do_unlock(&p_ctx->sid_mutex);
            continue;
        }
        else if (key_val == 'a')
        {
            do_lock(&p_ctx->sid_mutex);
            sample_ldt_cli_add(p_ctx);
            do_unlock(&p_ctx->sid_mutex);
            continue;
        }
        else if (key_val == 's')
        {
            do_lock(&p_ctx->sid_mutex);
            sample_ldt_cli_set_params(p_ctx);
            do_unlock(&p_ctx->sid_mutex);
            continue;
        }
    }

    if (p_ctx->io_running)
    {
        sample_ldt_cli_stop_ioctrl(p_ctx);
    }
    if (p_ctx->sid >= 0)
    {
        LDT_Cli_DisconnDevice(p_ctx->sid);
        logd("LDT_Cli_DisconnDevice ok, sid: %d", p_ctx->sid);
    }

_exit2_:
    LDT_Cli_Cleanup();

_exit1_:
    destroy_lock(&p_ctx->sid_mutex);
    destroy_lock(&p_ctx->ref_lock);

    return 0;
}

static void _sample_ldt_cli_usage(char * this)
{
    printf("\033[32m");
    printf("Usage: \n");
    printf("    %s -u [uid] -p [password] \n", this);
    printf("        -u [uid]        : the uid of ldt_device, defaut: '0001' \n");
    printf("        -p [password]   : the password of ldt_device, defaut: '666666' \n");
    printf("    such as: %s -u 0001 -p 666666 \n\n", this);
    printf("After %s is running, there are some useful command: \n", this);
    printf("        'q' or 'Q': exit this app \n");
    printf("        'f' or 'F': find ldt_devices \n");
    printf("        'c'       : connect the ldt_dev by 'uid' \n");
    printf("        'C'       : disconnect the ldt_dev \n");
    printf("        'g'       : create a channel group \n");
    printf("        'G'       : destory the channel group \n");
    printf("        'a'       : request a remote function, and get the result \n");
    printf("        's'       : request a setting/getting parames \n");
    printf("\033[0m\n");
}

int main(int argc, char *argv[])
{
    int ret = 0;
    int i = 0;
    char * uid = "0001";
    char * password = "666666";

    if ((argc == 2)
        && (!strcmp(argv[1], "-h")))
    {
        _sample_ldt_cli_usage(argv[0]);
        return 0;
    }

    for (i = 1; i < argc; )
    {
        if (!strcmp(argv[i], "-u"))
        {
            uid = argv[i+1];
            i += 2;
        }
        else if (!strcmp(argv[i], "-p"))
        {
            password = argv[i+1];
            i += 2;
        }
        else
        {
            i++;
        }
    }

    sample_ldt_cli_process(uid, password);

    logd("bye bye");

    return 0;
}
